package com.xinlong.shop.core.member.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xinlong.shop.core.member.entity.dto.MemberDTO;
import com.xinlong.shop.core.member.entity.dto.MemberListDTO;
import com.xinlong.shop.core.member.entity.dto.MemberSearchDTO;
import com.xinlong.shop.core.member.entity.vo.MemberLoginVO;
import com.xinlong.shop.core.member.mapper.MemberListDTOMapper;
import com.xinlong.shop.core.member.mapstruct.MemberStruct;
import com.xinlong.shop.core.member.service.IMemberService;
import com.xinlong.shop.framework.cache.ICache;
import com.xinlong.shop.framework.core.entity.Member;
import com.xinlong.shop.framework.core.mapper.MemberMapper;
import com.xinlong.shop.framework.core.model.LoginBuyer;
import com.xinlong.shop.framework.exception.ServiceException;
import com.xinlong.shop.framework.security.util.TokenUtil;
import com.xinlong.shop.framework.service.impl.UserDetailsServiceImpl;
import com.xinlong.shop.framework.sms.AliyunSMS;
import com.xinlong.shop.framework.util.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.List;
import java.util.concurrent.ExecutionException;

/**
 * <p>
 * 会员 服务实现类
 * </p>
 *
 * @author Sylow
 * @since 2022-11-04
 */
@Service
public class MemberServiceImpl extends ServiceImpl<MemberMapper, Member> implements IMemberService {

    private final UserDetailsServiceImpl userDetailsServiceImpl;

    private final TokenUtil tokenUtil;

    private final PasswordEncoder passwordEncoder;

    private final MemberStruct memberStruct;

    private final AliyunSMS aliyunSMS;

    private final ICache<Member> cache;

    private final MemberListDTOMapper memberListDTOMapper;

    //private String passwordPrefix = "xinglong_";

    private static final Logger logger = LoggerFactory.getLogger(MemberServiceImpl.class);

    public MemberServiceImpl(UserDetailsServiceImpl userDetailsServiceImpl, TokenUtil tokenUtil, PasswordEncoder passwordEncoder, MemberStruct memberStruct, AliyunSMS aliyunSMS, ICache<Member> cache, MemberListDTOMapper memberListDTOMapper) {
        this.userDetailsServiceImpl = userDetailsServiceImpl;
        this.tokenUtil = tokenUtil;
        this.passwordEncoder = passwordEncoder;
        this.memberStruct = memberStruct;
        this.aliyunSMS = aliyunSMS;
        this.cache = cache;
        this.memberListDTOMapper = memberListDTOMapper;
    }

    @Override
    public void update(MemberDTO memberDTO, Integer id) {
        Member mobileMember = this.findByMobile(memberDTO.getMobile());
        // 如果存在手机号  id又不是自己，说明是其他用户手机
        if (mobileMember != null && !mobileMember.getId().equals(id)) {
            throw new ServiceException("手机号已注册");
        }
        if (memberDTO.getPassword() != null && !memberDTO.getPassword().isEmpty()) {
            String password = memberDTO.getPassword();
            password = new BCryptPasswordEncoder().encode(password);
            memberDTO.setPassword(password);
        } else {
            memberDTO.setPassword(null);
        }
        memberDTO.setMemberName(null);  //用户名不让修改

        Member member = memberStruct.toMember(memberDTO);
        UpdateWrapper<Member> updateWrapper = new UpdateWrapper<>();
        updateWrapper.eq("id", id);
        this.update(member, updateWrapper);
    }

    @Override
    public void delete(Integer id) {
        UpdateWrapper<Member> deleteWrapper = new UpdateWrapper<>();
        deleteWrapper.eq("id", id);
        this.remove(deleteWrapper);
    }

    @Override
    public MemberDTO save(MemberDTO memberDTO) {

        Member mobileMember = this.findByMobile(memberDTO.getMobile());
        if (mobileMember != null) {
            throw new ServiceException("手机号已注册");
        }

        Member nameMember = this.findByMemberName(memberDTO.getMemberName());
        if (nameMember != null) {
            throw new ServiceException("用户名已存在");
        }

        String password = memberDTO.getPassword();

        password = new BCryptPasswordEncoder().encode(password);
        memberDTO.setPassword(password);
        Member member = memberStruct.toMember(memberDTO);
        member.setMemberNo("m_" + member.getMobile());
        this.save(member);
        return memberDTO;
    }

    @Override
    public Member findByMobile(String mobile) {
        QueryWrapper<Member> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("mobile", mobile);
        return this.getOne(queryWrapper);
    }

    @Override
    public Member findByMemberName(String memberName) {
        QueryWrapper<Member> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("member_name", memberName);
        return this.getOne(queryWrapper);
    }

//    @Override
//    public Member findByToken(String token) {
//        token = token.replace(SecurityConstants.TOKEN_PREFIX, "");
//        String userName = tokenUtil.getUserNameFromToken(token);
//        Member member = this.findMember(userName);
//        return member;
//    }

    @Override
    public Member findMember(String param) {
        QueryWrapper<Member> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("member_name", param)
                    .or()
                    .eq("mobile", param)
                    .or()
                    .eq("member_no", param);
        // 避免系统出错  导致bug，返回list，取第一个
        List<Member> list = this.list(queryWrapper);
        if (list.size() == 0) {
            return null;
        } else {
            return list.get(0);
        }
    }

    @Override
    public MemberListDTO findMemberDTOById(Integer id) {
        return this.memberListDTOMapper.findMemberById(id);
    }

    @Override
    public IPage page(MemberSearchDTO memberSearchDTO, IPage page) {
//        QueryWrapper<Member> queryWrapper = new QueryWrapper<>();
//        queryWrapper.like("member_name", memberSearchDTO.getMemberName());
//        queryWrapper.like("mobile", memberSearchDTO.getMobile());
//        queryWrapper.like("nickname", memberSearchDTO.getNickname());
//        return this.page(page, queryWrapper);
        return memberListDTOMapper.findMemberPage(page, memberSearchDTO);
    }

    @Override
    public MemberLoginVO login(String memberName, String password, boolean isVerify) {
        String token = null;
        try {
            // 传递过来的也许是手机号或者用户名
            Member member = this.findMember(memberName);
            if(member == null) {
                return null;
            }
            UserDetails userDetails = new LoginBuyer(member);
            if (isVerify && !passwordEncoder.matches(password, userDetails.getPassword())) {
                throw new BadCredentialsException("密码不正确");
            }
            UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
            SecurityContextHolder.getContext().setAuthentication(authentication);
            token = tokenUtil.generateToken(userDetails, false);
            MemberLoginVO memberLoginVO = new MemberLoginVO();
            memberLoginVO.setMember(member);
            memberLoginVO.setToken(token);
            return memberLoginVO;
        } catch (BadCredentialsException e) {
            logger.debug("登录异常:{}", e.getMessage());
        } catch (RuntimeException e) {
            logger.warn("登录异常:{}", e.getMessage());
        }
        return null;
    }

    @Override
    public String generateRefreshToken(String memberName, boolean refreshToken) {
        // memberName 也许是手机号
        Member member = this.findMember(memberName);
        UserDetails userDetails = new LoginBuyer(member);
        String token = tokenUtil.generateToken(userDetails, refreshToken);
        return token;
    }

    @Override
    public boolean sendSmsCode(String phone, String servicePrefix) {
        boolean result = true;
        String code = StringUtil.randomSmsCode();
        int expTime = 5 * 60;   // 过期时间 5分钟
        String key = servicePrefix + "_" + phone;   // 存入缓存中的key

        try {
            // 发送短信
            this.aliyunSMS.sendCode(phone, code);
            // 存入缓存
            this.cache.put(key, code, expTime);
        } catch (ExecutionException e) {
            logger.warn("短信发送异常：{}", e.getMessage());
            result = false;
        } catch (InterruptedException e) {
            e.printStackTrace();
            logger.warn("短信发送异常：{}", e.getMessage());
            result = false;
        }

        return result;
    }

    @Override
    public boolean verifySmsCode(String phone, String servicePrefix, String code) {
        String key = servicePrefix + "_" + phone;   // 存入缓存中的key
        Object cacheCodeObj = this.cache.get(key);
        if (cacheCodeObj == null) {
            return false;
        }
        String cacheCode = cacheCodeObj.toString();

        if (code.equals(cacheCode)) {
            this.cache.delete(key);
            return true;
        }

        return false;
    }

    @Override
    public void register(String phone, String password, String inviteMemberNo) {
        Member mobileMember = this.findMember(phone);
        if (mobileMember != null) {
            throw new ServiceException("手机号已注册");
        }
        String  memberNo = "m_" + phone;
        mobileMember = this.findMember(memberNo);
        // 这里用 用户编号去查找 如果查到了 说明手机号以前注册过 改变编号
        if (mobileMember != null) {
            // 随机数 会有个小bug  同一个手机号重复注册多次 会有会员编号重复的可能，情况比较少 不写循环查了
            String code = String.valueOf(StringUtil.random(100));
            memberNo = memberNo + "_" + code;
        }

        password = new BCryptPasswordEncoder().encode(password);

        // 邀请人
        Integer inviteMemberId = 0;
        if (StrUtil.isNotBlank(inviteMemberNo)) {
            Member inviteMember = this.findMember(inviteMemberNo);
            if (inviteMember != null) {
                inviteMemberId = inviteMember.getId();
            }
        }
        MemberDTO memberDTO = new MemberDTO();
        memberDTO.setMobile(phone);
        memberDTO.setPassword(password);
        Member member = memberStruct.toMember(memberDTO);
        member.setMemberNo(memberNo);
        member.setMemberName(memberNo);
        member.setNickname(memberNo);
        member.setGradeId(1);   // 注册默认等级暂时写死id  后期再改
        member.setInviteMemberId(inviteMemberId);
        member.setCreateTime(DateUtil.currentSeconds());

        this.save(member);
    }

    @Override
    public IPage<Member> inviteePage(Integer memberId, IPage<Member> page) {
        QueryWrapper<Member> query = new QueryWrapper<>();
        query.eq("invite_member_id", memberId);
        query.orderByDesc("create_time");
        return this.page(page, query);
    }

    @Override
    public Member getInviteeMemberByMemberId(Integer memberId) {
        // 先得到自己
        Member member = this.getById(memberId);
        // 再得到邀请人
        return this.getById(member.getInviteMemberId());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addBalance(Integer memberId, BigDecimal price) {
        // 先得到自己
        Member member = this.getById(memberId);
        BigDecimal newBalance = NumberUtil.add(member.getBalance(), price);
        member.setBalance(newBalance);
        this.updateById(member);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void subBalance(Integer memberId, BigDecimal price) {
        // 先得到自己
        Member member = this.getById(memberId);
        BigDecimal newBalance = NumberUtil.sub(member.getBalance(), price);
        member.setBalance(newBalance);
        this.updateById(member);
    }

    @Override
    public void updatePassword(Integer memberId, String password) {
        password = new BCryptPasswordEncoder().encode(password);
        UpdateWrapper<Member> updateWrapper = new UpdateWrapper<>();
        updateWrapper.set("password", password);
        updateWrapper.eq("id", memberId);
        this.update(updateWrapper);
    }

    @Override
    public void updateStatus(Integer memberId, Integer status) {
        UpdateWrapper<Member> updateWrapper = new UpdateWrapper<>();
        updateWrapper.set("status", status);
        updateWrapper.eq("id", memberId);
        this.update(updateWrapper);
    }

    @Override
    public void updateVip(Integer memberId, Integer isVip) {
        UpdateWrapper<Member> updateWrapper = new UpdateWrapper<>();
        updateWrapper.set("is_vip", isVip);
        updateWrapper.eq("id", memberId);
        this.update(updateWrapper);
    }

    @Override
    public void updateBalance(Integer memberId, BigDecimal price) {
        /**
         * 直接修改余额 需求上说不增加日志
         */
        UpdateWrapper<Member> updateWrapper = new UpdateWrapper<>();
        updateWrapper.set("balance", price);
        updateWrapper.eq("id", memberId);
        this.update(updateWrapper);
    }


}
